<?php

namespace App\Http\Middleware;

use App\Constant;
use App\Enums\AvailableStatus;
use App\Models\Agent;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;

class AgentGateway
{
    /** @var int */
    private $startTime;

    /** @var Request */
    private $request;

    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next)
    {
        $this->startTime = Carbon::now()->getTimestampMs();
        $this->request = $request;

        $appId = $request->header('AppId');
        $sign = $request->header('Signature');
        $method = $request->getMethod();

        if (!$appId) {
            return $this->failResponse('请求头中不包含AppId');
        }
        if (!$sign) {
            return $this->failResponse('请求头中不包含Signature');
        }
        $agent = \Cache::remember('agent_app_' . $appId, 60 * 5, function () use ($appId) {
            return Agent::query()->where('app_id', $appId)->first();
        });
        if (!$agent) {
            return $this->failResponse('无效的AppId');
        }
        if ($agent->status->is(AvailableStatus::DISABLED())) {
            return $this->failResponse('AppId不可用');
        }

        $params = 'GET' === $method ? $request->query() : $request->json()->all();
        ksort($params);
        $keyValues = [];
        foreach ($params as $key => $value) {
            if (is_null($value) || '' === $value) {
                continue;
            }
            if (is_array($value)) {
                $value = json_encode($value);
            }
            $keyValues[] = $key . '=' . $value;
        }
        $str = implode('&', $keyValues);
        $signStr = $str . '_' . $agent->app_secret;
        if ('chainxtech' != $sign && $sign != md5($signStr)) {
            return $this->failResponse('接口签名错误');
        }

        // 将agent_id添加请求参数中，方便在业务层使用
        $request->merge([
            'agent_id' => $agent->id,
        ]);

        $resp = $next($request);
        $this->accessLog();

        return $resp;
    }

    private function failResponse($msg)
    {
        $resp = new JsonResponse();
        $this->accessLog();

        return $resp->setData([
            'code' => 'agent_sign_failed',
            'msg' => $msg,
            'data' => null,
        ]);
    }

    private function accessLog()
    {
        $request = $this->request;
        $cost = Carbon::now()->getTimestampMs() - $this->startTime;
        \Log::channel(Constant::LOG_CHANNEL_ACCESS)->info(
            sprintf('%s:%s, COST: %sms', $request->getMethod(), $request->url(), $cost),
            [
                'query' => $request->query(),
                'post' => $request->json()->all(),
                'headers' => [
                    'Content-Type' => $request->header('Content-Type'),
                    'AppId' => $request->header('AppId'),
                    'Signature' => $request->header('Signature'),
                    'Authorization' => $request->header('Authorization'),
                ],
            ]
        );
    }
}
